#ifndef LIR_H
#define LIR_H

#include <opcode.h>
#include <flow.h>
#include <list>
#include <map>

typedef enum LirCode{
    ADD,
    SUB,
    MUL,
    DIV,
    MOV,
    JMP,                        // 0(dst), <addressoperand>arg1
    CALL,
    PUSH,                       // arg1: the register to push
    POP,                        // arg1: the register which pop to
    SPILL_LOAD,                 // reg(dst) <= [ebp+offset(arg1,constantoperand)]
    SPILL_STORE,                // [ebp+offset(dst,constantoperand)] <= reg[arg1]
    LOADCONST,                  // reg <= constant_index(constantoperand) in pool
    TEST,
    JNE,
    ORri,                       // or reg, immediate
}LC;

class Lir;
class LirQueue;
class Label;

/// @brief the operand used in lir
class Operand
{
protected:
    Lir *owner;

public:
    virtual bool isRegOperand()
    {
        return false;
    }

    virtual bool isConstantOperand()
    {
        return false;
    }

    virtual bool isAddressOperand()
    {
        return false;
    }

    Operand()
    {}

};

class RegOperand : public Operand
{
private:
    // the virtual register number
    int regNum;

public:
    virtual bool isRegOperand()
    {
        return true;
    }

    operator int() const
    {
        return regNum;
    }

    RegOperand& operator=(int reg)
    {
        regNum = reg;
    }

    bool operator==(const RegOperand& reg) const
    {
        int num = reg;
        return regNum == num;
    }

    RegOperand(int num):regNum(num)
    {}
};

template <typename T>
class ConstantOperand : public Operand
{
private:
    T data;
public:
    virtual bool isConstantOperand()
    {
        return true;
    }

    ConstantOperand(T d):data(d)
    {}

    operator const T()
    {
        return data;
    }
};

class AddressOperand : public Operand
{
private:

public:
    virtual bool isAddressOperand()
    {
        return true;
    }

    // where operand pointing to
    Label *label;

    AddressOperand(Label *l):label(l)
    {}
};

/// @brief label references to the lir position
class Label
{
public:
    Lir *lir;

    Label(Lir *l):lir(l)
    {}
};

/// @brief  low-level intermediate representation
class Lir
{
public:
    LC type;

    Operand *dst,*arg1,*arg2;

    Lir(LC t,Operand *d = 0,Operand *a1 = 0,Operand *a2 = 0);
    void removeFromQueue(LirQueue& q);
};

/// @brief  queue specific to lir
class LirQueue
{
private:
    std::list<Lir*> lirs;

public:
    typedef std::list<Lir*>::iterator  iterator;
    typedef std::list<Lir*>::reverse_iterator reverse_iterator;
    typedef std::list<Lir*>::const_iterator const_iterator;

    iterator getPosition(const Lir *l);
    void add(Lir *l);
    void insert(LirQueue::iterator it,Lir *l);
    void remove(Lir *l);
    void erase(LirQueue::iterator lit);
    void clear();
    int  distance(const Lir *l1,const Lir *l2);

    const iterator begin()
    {
        return lirs.begin();
    }

    const iterator end()
    {
        return lirs.end();
    }

    const reverse_iterator rbegin()
    {
        return lirs.rbegin();
    }

    const reverse_iterator rend()
    {
        return lirs.rend();
    }

    LirQueue()
    {}
};

class X86Encoder
{
private:
    LirQueue lirs;
    LC getOperator(Opcode *o);

    void add(Lir *lir)
    {
        lirs.add(lir);
    }

    static int pc;

    /// record the pc to Label mapping
    static std::map<int,Label*> pcToLabel;

    /// record the every pc(opcode)'s firstLir
    static std::map<int,Lir*> pcToLir;
public:
    /// record the labels at a lir's postion
    static std::map<Lir*,Label*> labelMap;

    void encode(BasicBlock *bb);

    LirQueue getLirs()
    {
        return lirs;
    }

    static void fixupLabels();
};

#endif

